package earlyDetection;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

public class Automata {
    private HashSet<Integer> states;
    private HashSet<String> symbols;
    private HashSet<Integer> finals;
    private HashMap<Integer, HashMap<String, Integer>> transfers;
    private int currentState;

    public Automata(HashSet<String> symbols) {
        this.states = new HashSet<>();
        this.symbols = symbols;
        this.finals = new HashSet<>();
        this.transfers = new HashMap<>();
        this.addState(0); // start state
        this.currentState = 0;
//        this.addState(-1); // error state
    }

    public static Automata fromString(String regex, HashSet<String> symbols) {
        Automata automata = new Automata(symbols);

        String[] elements = regex.split(",");
        int idx = 1;
        Integer lastState = 0;
        for (String element : elements) {
            if (element.equals(".*")) {
                for (String sym : symbols) {
                    automata.addTransfer(lastState, sym, lastState);
                }
            } else if (element.startsWith(".")) { // e.g., .3
                for (int i = 0; i < Integer.parseInt(element.substring(1)); i++) {
                    int newState = idx++;
                    automata.addState(newState);
                    for (String sym : symbols) {
                        automata.addTransfer(lastState, sym, newState);
                    }
                    lastState = newState;
                }
            } else if (element.contains("|")) {
                int newState = idx++;
                automata.addState(newState);
                for (String e : element.split("\\|")) {
                    automata.addTransfer(lastState, e, newState);
                }
                lastState = newState;
            } else {
                int newState = idx++;
                automata.addState(newState);
                automata.addTransfer(lastState, element, newState);
                lastState = newState;
            }
        }
        HashSet<Integer> finals = new HashSet<>();
        finals.add(lastState);
        automata.setFinals(finals);
        return automata;
    }

    public void addState(Integer label) {
        this.states.add(label);
        this.transfers.put(label, new HashMap<>());

        // we do not need to traverse the automata, DFA is not needed
        // set all transfer to error state, will be overwritten by addTransfer
//        for (String sym : symbols) {
//            this.transfers.get(label).put(sym, -1);
//        }
    }

    public void addTransfer(Integer src, String symbol, Integer dst) {
        this.transfers.get(src).put(symbol, dst);
    }

    public HashSet<Integer> getStates() {
        return states;
    }

    public int takeActionOnState(int state, String action) {
        return this.transfers.get(state).getOrDefault(action, -1);
    }

    public int take(String action) {
        currentState = this.transfers.get(currentState).getOrDefault(action, -1);
        return currentState;
    }

    public int getCurrentState() {
        return currentState;
    }

    public void setCurrentState(int currentState) {
        this.currentState = currentState;
    }

    public boolean isAccept() {
        return this.finals.contains(currentState);
    }

    public HashSet<Integer> getFinals() {
        return finals;
    }

    public void setFinals(HashSet<Integer> finals) {
        this.finals = finals;
    }

    public static void main(String[] args) {
        HashSet<String> symbols = new HashSet<>(Arrays.asList("a", "b", "c", "d"));
        Automata a = Automata.fromString("a,.*,d", symbols);
        long s = System.nanoTime();
        for (int i = 0; i < 1; i++) {
            Automata b = Automata.fromString("a,b,d", symbols);
//            Automata ab = a.product(b);
//            System.out.println(ab.checkValid());
        }
        System.out.println(System.nanoTime() - s);
    }
}
